﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Practices.Unity;
using Wyn.PMS.Common.Utils;
using Wyn.PMS.Entity.DataAccess;
using Wyn.PMS.Entity.Entities;
using Wyn.PMS.Entity.IDataAccess;

namespace Wyn.PMS.Common.NightAudit
{
    internal class OperationStatus
    {
        private static readonly IUnityContainer container = UnityContainerService.GetContainer();

        internal IList<NaOperationStatus> Snapshot(HotelBizDay bizDay, IList<RoomType> roomTypes, IList<Account> notRollAccounts, IDictionary<string, decimal> rollRoomPrice, IDictionary<string, decimal> rtRoomFeeAccounts,IList<Order> rollAccountOrders, IList<Order> allOrders)
        {
            INaOperationStatusRepository statusRepository = container.Resolve<INaOperationStatusRepository>();

            IRoomOccupyRepository roomOccupyRepository = container.Resolve<IRoomOccupyRepository>();

            IReserveRepository resvRepository = container.Resolve<IReserveRepository>();

            IOrderRepository orderRepository = container.Resolve<IOrderRepository>();

            var transactionOptions = new System.Transactions.TransactionOptions();

            transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
            
            string hotelId = bizDay.HotelID;

            var rsMonitor = CacheMonitor.GetInstance();

            var rooms = rsMonitor.GetCacheRooms(GlobalConst.DEFAULT_START_TIME, hotelId).Value;

            DateTime todayBizDay = bizDay.BizDay;

            DateTime todayBizMax = todayBizDay.AddDays(1).AddSeconds(-1);

            IDictionary<string, Expression<Func<RoomOccupy, bool>>> exps = BuildWillInRoomTypeCountExp(todayBizDay, roomTypes);

            //获取预订将到数
            IDictionary<string, int> willInRoomTypeCounts = roomOccupyRepository.CountRoomTypeOccupies(hotelId, exps);

            string resvStatusX = EnumHelper.GetCode(ReserveStatus.X);

            string resvStatusR = EnumHelper.GetCode(ReserveStatus.R);

            string resvStatusN = EnumHelper.GetCode(ReserveStatus.N);

            int orderTypeR = (int)OrderType.Reserve;

            //获取今天下的预订单 如果失效则需要是取消的和NOSHOW的
            Expression<Func<RoomOccupy, bool>> resvExp =
                r => r.HotelID == hotelId
                     && r.OrderType == orderTypeR
                     && r.BookBizDay == todayBizDay
                     && (r.IsValid || (!r.IsValid && (r.IsCancel || r.IsNoShow)));

            //今日取消预订房数
            Expression<Func<RoomOccupy, bool>> cancelExp =
                r => r.HotelID == hotelId
                     && r.CancelBizDay == todayBizDay
                     && r.OrderType == orderTypeR
                     && r.IsCancel;

            /*
            Expression<Func<Reservation, bool>> nowShowExp1 =
                r => r.HotelID == hotelId
                     && (r.StartDate >= todayBizDay && r.StartDate <= todayBizMax
                        && ((r.Status == resvStatusR && r.IsGuarantee == false) || r.Status == resvStatusN))
                     || (r.StartDate < todayBizDay && r.Status == resvStatusR)
                     || (r.StartDate < todayBizDay && r.ModifiedTime >= todayBizDay && r.Status == resvStatusN);
            */

            //获取NoShow单 (有担保的预订状态单不进noshow)
            Expression<Func<Reservation, bool>> nowShowExp =
                r => r.HotelID == hotelId
                     &&( ( r.StartDate < todayBizMax && r.Status == resvStatusR && r.IsGuarantee == false)
                     ||r.ModifiedTime>=todayBizDay && r.Status==resvStatusN);

            //构建营业日占用房量表达式
            IDictionary<string, Expression<Func<RoomOccupy, bool>>> dayOccupyExps = BuildDayOccupyCountExp(todayBizDay, roomTypes);

            IList<RoomOccupy> resvOccupies = new List<RoomOccupy>();

            IList<RoomOccupy> todayCancelOccupies = new List<RoomOccupy>();

            IList<Reservation> noShowResvation = new List<Reservation>();

            //获取营业日占用房量
            IDictionary<string, int> dayOccupyCounts = new Dictionary<string, int>();
            
            using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
            {
                resvOccupies = roomOccupyRepository.BatchFind(hotelId, resvExp);

                todayCancelOccupies = roomOccupyRepository.BatchFind(hotelId, cancelExp);

                dayOccupyCounts = roomOccupyRepository.CountRoomTypeOccupies(hotelId, dayOccupyExps);

                noShowResvation = resvRepository.BatchIncludeFind(hotelId, nowShowExp);
                
                scope.Complete();
            }

            //获取Noshow的房控
            IList<RoomOccupy> noShowOccupies = BuildNoShowOccupies(noShowResvation);

            //声明状态快照容器
            IList<NaOperationStatus> statuses = new List<NaOperationStatus>();

            IList<RollAccountOrderCount> rollAccountOrderCounts = CountRollAccountOrder(notRollAccounts, rollAccountOrders, roomTypes);

            //平均房价计算器
            IDictionary<string, decimal> averageRoomPrice = BuildAverageRoomPrice(rtRoomFeeAccounts, notRollAccounts, rollAccountOrders, roomTypes, rollAccountOrderCounts, bizDay);
            
            foreach (var roomType in roomTypes)
            {
                var roomTypeRooms = rooms.Where(r => r.RoomTypeID == roomType.RoomTypeID).ToList();

                IEnumerable<RoomOccupy> roomTypeResvOccupies = resvOccupies.Where(o => o.RoomTypeID == roomType.RoomTypeID);

                int roomCount = dayOccupyCounts.FirstOrDefault(r => r.Key == roomType.RoomTypeID).Value;

                var roomTypeOrders = allOrders.Where(o => o.RoomTypeID == roomType.RoomTypeID).ToList();

                var roomTypeAccountOrderCount = rollAccountOrderCounts.FirstOrDefault(o => o.Name == roomType.RoomTypeID);

                NaOperationStatus status = BuildStatus(bizDay, roomType, roomTypeOrders, roomTypeAccountOrderCount, roomTypeRooms, roomTypeResvOccupies, roomCount);
                
                status.NoShow = noShowOccupies.Count(r => r.RoomTypeID == roomType.RoomTypeID);

                status.ResvCancel = todayCancelOccupies.Count(r => r.RoomTypeID == roomType.RoomTypeID);

                status.WillArriveCount = willInRoomTypeCounts.FirstOrDefault(r => r.Key == roomType.RoomTypeID).Value;

                if (rollRoomPrice != null)
                {
                    status.AuditRoomPrice = rollRoomPrice[status.RoomTypeID];
                }

                status.TodayRoomReceipts = rtRoomFeeAccounts.FirstOrDefault(r => r.Key == status.RoomTypeID).Value;
                
                status.RoomTypePrice = averageRoomPrice.FirstOrDefault(r => r.Key == roomType.RoomTypeID).Value;

                statuses.Add(status);
            }

            //TODO:对NOSHOW进行扣分处理 等等。
            //更新状态和获取排房order
            var noShowOrders = UpdateStatusAndGetOrders(noShowResvation, noShowOccupies, bizDay);

            if (bizDay.Status != EnumHelper.GetCode(NightAuditCode.Status.E)
                || (bizDay.Status == EnumHelper.GetCode(NightAuditCode.Status.E)
                && bizDay.Steping == (int)NightAuditCode.Steping.OperationStatus))
            {
                bizDay.Status = EnumHelper.GetCode(NightAuditCode.Status.I);

                using (TransactionScope scope = new TransactionScope())
                {
                    statusRepository.BatchInsert(hotelId, statuses);

                    //TODO:NOSHOW扣分
                    resvRepository.BatchUpdate(hotelId, noShowResvation);
                    
                    roomOccupyRepository.BatchUpdate(hotelId, noShowOccupies);

                    orderRepository.BatchUpdate(hotelId, noShowOrders);

                    scope.Complete();
                }
            }

            return statuses;
        }

        private NaOperationStatus BuildStatus(HotelBizDay bizDay, RoomType roomType, IList<Order> orders, RollAccountOrderCount rollOrderCount, IList<Room> rooms, IEnumerable<RoomOccupy> resvOccupies, int occupyCount)
        {
            NaOperationStatus status = new NaOperationStatus();

            status.HotelID = bizDay.HotelID;

            status.BizDay = bizDay.BizDay;

            status.RoomTypeID = roomType.RoomTypeID;

            status.RoomTypeName = roomType.RoomTypeName;

            status.Count = roomType.TotalRooms;

            status.OccupiesCount = occupyCount;

            var allOOO = rooms.Where(r => r.Status == EnumHelper.GetCode(RoomStatus.OOO));

            if (allOOO.Any())
            {
                status.OOOCount = allOOO.Count(r =>
                {
                    var firstOrDefault = r.RoomAttribute.FirstOrDefault(a => a.AttributeCode == EnumHelper.GetCode(RoomAttributeCode.RP));
                    return firstOrDefault == null || firstOrDefault.IsValid == false;
                });

                status.RepairCount = allOOO.Count() - status.OOOCount;
            }

            status.LockCount = rooms.Count(r =>
            {
                var roomAttribute = r.RoomAttribute.FirstOrDefault(a =>
                    a.AttributeCode == EnumHelper.GetCode(RoomAttributeCode.LR));
                return roomAttribute != null && roomAttribute.IsValid;
            });

            status.VDCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.VD));

            status.VCCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.VC));

            status.ODCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.OD));

            status.OCCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.OC));

            //营业日内结账
            status.CheckOutCount =  orders.Count(a => a.CheckOutBizDay.HasValue && a.CheckOutBizDay.Value == bizDay.BizDay);

            //提前离店
            status.EarlyCheckOut = orders.Count(a => a.CheckOutTime.HasValue
                                                           && a.CheckOutTime.Value.Date < a.DepartureDate.Date
                                                           && a.CheckOutBizDay.Value == bizDay.BizDay);

            status.CheckInAndOut = orders.Count(a => a.CheckOutBizDay.HasValue && a.CheckInBizDay.Value == a.CheckOutBizDay.Value && a.CheckOutBizDay.Value == bizDay.BizDay);

            status.CheckInCount = orders.Count(a => a.CheckInBizDay.HasValue && a.CheckInBizDay.Value == bizDay.BizDay);

            DateTime tomorrow = bizDay.BizDay.AddDays(2).AddSeconds(-1);

            status.WillLeaveCount = orders.Count(a => a.Status == EnumHelper.GetCode(OrderStatus.I) && a.DepartureDate <= tomorrow);

            status.ResvArrived = orders.Count(a => a.CheckInBizDay == bizDay.BizDay && !string.IsNullOrEmpty(a.ResvID));

            status.TodayLeave = orders.Count(a => a.CheckOutBizDay.HasValue && a.CheckOutBizDay.Value == bizDay.BizDay);

            //TODO:看要不要减去noshow量
            status.ResvCount = resvOccupies.Count();

            status.ResvAssigned = resvOccupies.Count(o => !string.IsNullOrEmpty(o.RoomNo));

            //TODO:看是统计今日预订的量还是统计所有有效预订的量
            status.ResvRoomCount = CountPassNight(resvOccupies);

            status.HourRoomCount = orders.Count(a =>a.CheckInType == EnumHelper.GetCode(CheckInType.HR));

            status.DayRoomCount = orders.Count(a => a.CheckInType == EnumHelper.GetCode(CheckInType.DR));

            status.FreeRoomCount = orders.Count(a => a.CheckInType == EnumHelper.GetCode(CheckInType.FR));

            status.SelfRoomCount = orders.Count(a =>a.CheckInType == EnumHelper.GetCode(CheckInType.SU));

            status.AgentRoomCount = orders.Count(a =>!string.IsNullOrWhiteSpace(a.MemberNo));

            status.ExtraHalfDayPriceCount = rollOrderCount.ExtraHalfDay;

            status.ExtraPassNightCount = rollOrderCount.ExtraFullDay;

            //超预订数 = 总房量-总占量 为负时的绝对值
            int wait = (roomType.TotalRooms - occupyCount) < 0 ? Math.Abs(roomType.TotalRooms - occupyCount) : 0;

            status.ResvWait = wait;

            return status;
        }
        
        /// <summary>
        /// 旧方法[已废弃]
        /// </summary>
        /// <param name="bizDay"></param>
        /// <param name="roomTypes"></param>
        /// <param name="accountRoots"></param>
        /// <param name="rollRoomPrice"></param>
        /// <param name="rtRoomFeeAccounts"></param>
        /// <returns></returns>
        public IList<NaOperationStatus> Snapshot(HotelBizDay bizDay, IList<RoomType> roomTypes, IEnumerable<AccountRoot> accountRoots, IDictionary<string, decimal> rollRoomPrice, IDictionary<string, decimal> rtRoomFeeAccounts)
        {
            INaOperationStatusRepository statusRepository = container.Resolve<INaOperationStatusRepository>();

            IRoomOccupyRepository roomOccupyRepository = container.Resolve<IRoomOccupyRepository>();

            IReserveRepository resvRepository = container.Resolve<IReserveRepository>();

            IOrderRepository orderRepository = container.Resolve<IOrderRepository>();
            
            string hotelId = bizDay.HotelID;

            var rsMonitor = CacheMonitor.GetInstance();

            var rooms = rsMonitor.GetCacheRooms(GlobalConst.DEFAULT_START_TIME,hotelId).Value;
            
            DateTime todayBizDay = bizDay.BizDay;

            DateTime todayBizMax = todayBizDay.AddDays(1).AddSeconds(-1);

            IDictionary<string, Expression<Func<RoomOccupy, bool>>> exps = BuildWillInRoomTypeCountExp(todayBizDay,roomTypes);

            //获取预订将到数
            IDictionary<string, int> willInRoomTypeCounts = roomOccupyRepository.CountRoomTypeOccupies(hotelId, exps);
            
            string resvStatusX = EnumHelper.GetCode(ReserveStatus.X);

            string resvStatusR = EnumHelper.GetCode(ReserveStatus.R);

            string resvStatusN = EnumHelper.GetCode(ReserveStatus.N);

            int orderTypeR = (int) OrderType.Reserve;

            //获取今天下的预订单 如果失效则需要是取消的和NOSHOW的
            Expression<Func<RoomOccupy, bool>> resvExp =
                r => r.HotelID == hotelId
                     && r.OrderType == orderTypeR
                     && r.BookBizDay == todayBizDay
                     && (r.IsValid || (!r.IsValid && (r.IsCancel || r.IsNoShow)));

            var roomOccupies = roomOccupyRepository.BatchFind(hotelId, resvExp);
            
            //今日取消预订房数
            Expression<Func<RoomOccupy, bool>> cancelExp =
                r => r.HotelID == hotelId
                     && r.CancelBizDay == todayBizDay
                     && r.OrderType == orderTypeR
                     && r.IsCancel;

            var todayCancelOccupies = roomOccupyRepository.BatchFind(hotelId, cancelExp);
            
            //获取NoShow单 (有担保的预订状态单不进noshow)
            Expression<Func<Reservation, bool>> nowShowExp =
                r => r.HotelID == hotelId
                     && (r.StartDate >= todayBizDay && r.StartDate <= todayBizMax
                        && ((r.Status == resvStatusR && r.IsGuarantee == false) || r.Status == resvStatusN))
                     || (r.StartDate < todayBizDay && r.Status == resvStatusR)
                     || (r.StartDate < todayBizDay && r.ModifiedTime>=todayBizDay && r.Status == resvStatusN);
                     

            IList<Reservation> noShowResvation = resvRepository.BatchIncludeFind(hotelId, nowShowExp);
            
            //获取Noshow的房控
            IList<RoomOccupy> noShowOccupies = BuildNoShowOccupies(noShowResvation);

            //声明状态快照容器
            IList<NaOperationStatus> statuses = new List<NaOperationStatus>();

            //平均房价计算器
            IDictionary<string, decimal> averageRoomPrice = BuildAverageRoomPrice(accountRoots, roomTypes,bizDay);

            //构建营业日占用房量表达式
            IDictionary<string, Expression<Func<RoomOccupy, bool>>> dayOccupyExps = BuildDayOccupyCountExp(todayBizDay, roomTypes);

            //获取营业日占用房量
            IDictionary<string, int> dayOccupyCounts = roomOccupyRepository.CountRoomTypeOccupies(hotelId, dayOccupyExps);
            
            foreach (var roomType in roomTypes)
            {
                var roomTypeAccountRoots = accountRoots.Where(a => a.Order.RoomTypeID == roomType.RoomTypeID);

                var roomTypeRooms = rooms.Where(r => r.RoomTypeID == roomType.RoomTypeID);

                IEnumerable<RoomOccupy> roomTypeOccupies = roomOccupies.Where(o => o.RoomTypeID == roomType.RoomTypeID);

                int roomCount = dayOccupyCounts.FirstOrDefault(r => r.Key == roomType.RoomTypeID).Value;

                NaOperationStatus status = BuildStatus(bizDay, roomType, roomTypeRooms,
                                                       roomTypeAccountRoots, roomTypeOccupies, roomCount);

                status.NoShow = noShowOccupies.Count(r => r.RoomTypeID == roomType.RoomTypeID);

                status.ResvCancel = todayCancelOccupies.Count(r => r.RoomTypeID == roomType.RoomTypeID);

                status.WillArriveCount = willInRoomTypeCounts.FirstOrDefault(r => r.Key == roomType.RoomTypeID).Value;

                if (rollRoomPrice != null)
                {
                    status.AuditRoomPrice = rollRoomPrice[status.RoomTypeID];
                }

                status.TodayRoomReceipts = rtRoomFeeAccounts.FirstOrDefault(r => r.Key == status.RoomTypeID).Value;

                //计算平均房价
                status.RoomTypePrice = averageRoomPrice.FirstOrDefault(r => r.Key == roomType.RoomTypeID).Value;

                statuses.Add(status);
            }

            //TODO:对NOSHOW进行扣分处理 等等。

            //更新状态和获取排房order
            var noShowOrders = UpdateStatusAndGetOrders(noShowResvation, noShowOccupies, bizDay);

            if (bizDay.Status != EnumHelper.GetCode(NightAuditCode.Status.E)
                || (bizDay.Status == EnumHelper.GetCode(NightAuditCode.Status.E)
                && bizDay.Steping == (int)NightAuditCode.Steping.OperationStatus))
            {
                bizDay.Status = EnumHelper.GetCode(NightAuditCode.Status.I);

                using (TransactionScope scope = new TransactionScope())
                {
                    statusRepository.BatchInsert(hotelId, statuses);

                    resvRepository.BatchUpdate(hotelId, noShowResvation);

                    //TODO:NOSHOW扣分

                    roomOccupyRepository.BatchUpdate(hotelId, noShowOccupies);

                    orderRepository.BatchUpdate(hotelId, noShowOrders);

                    scope.Complete();
                }
            }

            return statuses;
        }
        
        /// <summary>
        /// 旧方法[作废]
        /// </summary>
        /// <param name="bizDay"></param>
        /// <param name="roomType"></param>
        /// <param name="rooms"></param>
        /// <param name="accountRoots"></param>
        /// <param name="resvOccupies"></param>
        /// <param name="occupyCount"></param>
        /// <returns></returns>
        private NaOperationStatus BuildStatus(HotelBizDay bizDay,RoomType roomType, IEnumerable<Room> rooms,IEnumerable<AccountRoot> accountRoots,IEnumerable<RoomOccupy> resvOccupies,int occupyCount)
        {
            NaOperationStatus status = new NaOperationStatus();

            status.HotelID = bizDay.HotelID;

            status.BizDay = bizDay.BizDay;

            status.RoomTypeID = roomType.RoomTypeID;

            status.RoomTypeName = roomType.RoomTypeName;

            status.Count = roomType.TotalRooms;

            status.OccupiesCount = occupyCount;

            var allOOO = rooms.Where(r => r.Status == EnumHelper.GetCode(RoomStatus.OOO));

            if (allOOO.Any())
            {
                status.OOOCount = allOOO.Count(r =>
                                                   {
                                                       var firstOrDefault =r.RoomAttribute.FirstOrDefault(a =>a.AttributeCode ==EnumHelper.GetCode(RoomAttributeCode.RP));
                                                       return firstOrDefault == null || firstOrDefault.IsValid == false;
                                                   });

                status.RepairCount = allOOO.Count() - status.OOOCount;
            }

            status.LockCount = rooms.Count(r =>
                                               {
                                                   var roomAttribute = r.RoomAttribute.FirstOrDefault(a =>
                                                           a.AttributeCode == EnumHelper.GetCode(RoomAttributeCode.LR));
                                                   return roomAttribute != null && roomAttribute.IsValid;
                                               });

            status.VDCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.VD));

            status.VCCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.VC));

            status.ODCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.OD));

            status.OCCount = rooms.Count(r => r.Status == EnumHelper.GetCode(RoomStatus.OC));

            //营业日内结账
            status.CheckOutCount = accountRoots.Count(a => a.Order.CheckOutBizDay.HasValue && a.Order.CheckOutBizDay.Value == bizDay.BizDay);

            status.EarlyCheckOut = accountRoots.Count(a => a.Order.CheckOutTime.HasValue
                                                           && a.Order.CheckOutTime.Value.Date != a.Order.DepartureDate.Date
                                                           && a.Order.CheckOutTime.Value.Hour <=GlobalConst.DEFAULT_CHECK_IN_HOUR
                                                           && a.Order.CheckOutBizDay.Value == bizDay.BizDay);

            status.CheckInAndOut = accountRoots.Count(a=> a.Order.CheckOutBizDay.HasValue && a.Order.CheckInBizDay.Value==a.Order.CheckOutBizDay.Value && a.Order.CheckOutBizDay.Value==bizDay.BizDay);

            status.CheckInCount = accountRoots.Count(a => a.Order.CheckInBizDay.HasValue && a.Order.CheckInBizDay.Value == bizDay.BizDay);
            
            DateTime tomorrow = bizDay.BizDay.AddDays(2).AddSeconds(-1);

            status.WillLeaveCount =accountRoots.Count(a => a.Order.Status == EnumHelper.GetCode(OrderStatus.I) && a.Order.DepartureDate <= tomorrow);

            status.ResvArrived = accountRoots.Count(a => a.Order.CheckInBizDay == bizDay.BizDay && !string.IsNullOrEmpty(a.Order.ResvID));

            status.TodayLeave = accountRoots.Count(a => a.Order.CheckOutBizDay.HasValue && a.Order.CheckOutBizDay.Value == bizDay.BizDay);

            //TODO:看要不要减去noshow量
            status.ResvCount = resvOccupies.Count();

            status.ResvAssigned = resvOccupies.Count(o => !string.IsNullOrEmpty(o.RoomNo));

            //TODO:看是统计今日预订的量还是统计所有有效预订的量
            status.ResvRoomCount = CountPassNight(resvOccupies);

            status.HourRoomCount = accountRoots.Count(a =>
                                                      a.Order.CheckInBizDay == bizDay.BizDay &&
                                                      a.Order.CheckInType == EnumHelper.GetCode(CheckInType.HR));

            status.DayRoomCount = accountRoots.Count(a =>
                                                     a.Order.CheckInBizDay == bizDay.BizDay &&
                                                     a.Order.CheckInType == EnumHelper.GetCode(CheckInType.DR));

            status.FreeRoomCount = accountRoots.Count(a =>
                                                      a.Order.CheckInBizDay == bizDay.BizDay &&
                                                      a.Order.CheckInType == EnumHelper.GetCode(CheckInType.FR));

            status.SelfRoomCount = accountRoots.Count(a =>
                                                      a.Order.CheckOutBizDay == bizDay.BizDay &&
                                                      a.Order.CheckInType == EnumHelper.GetCode(CheckInType.SU));
            
            status.AgentRoomCount = accountRoots.Count(a =>
                                                       a.Order.CheckOutBizDay == bizDay.BizDay &&
                                                       (a.Order.GuestSourceType ==EnumHelper.GetCode(GuestSourceType.AM)||
                                                        a.Order.GuestSourceType ==EnumHelper.GetCode(GuestSourceType.CM)));

            status.ExtraHalfDayPriceCount = accountRoots.Count(a => a.Order.CheckOutBizDay == bizDay.BizDay && a.Account.Any(k => k.LedgerCode == LedgerCode.半日租));
            
            status.ExtraPassNightCount = accountRoots.Count(a => a.Order.CheckOutBizDay == bizDay.BizDay && a.Account.Any(k => k.LedgerCode == LedgerCode.全日租));
            
            //超预订数 = 总房量-总占量 为负时的绝对值
            int wait = (roomType.TotalRooms - occupyCount) < 0 ? Math.Abs(roomType.TotalRooms - occupyCount) : 0;
            
            status.ResvWait = wait;
            
            return status;

        }
        
        private IDictionary<string, Expression<Func<RoomOccupy, bool>>> BuildWillInRoomTypeCountExp(DateTime bizDay,IList<RoomType> roomTypes)
        {
            IDictionary<string, Expression<Func<RoomOccupy, bool>>> exps =
                new Dictionary<string, Expression<Func<RoomOccupy, bool>>>();

            DateTime tomorrow = bizDay.AddDays(1);

            DateTime tomorrowMax = bizDay.AddDays(2).AddSeconds(-1);

            foreach(var type in roomTypes)
            {
                string roomTypeId = type.RoomTypeID;

                string hotelId = type.HotelID;

                int orderTypeR = (int) OrderType.Reserve;

                Expression<Func<RoomOccupy, bool>> exp = r => r.HotelID == hotelId
                                                              && r.RoomTypeID == roomTypeId
                                                              && r.OrderType == orderTypeR
                                                              && r.StartDate >= tomorrow
                                                              && r.StartDate <= tomorrowMax
                                                              && r.IsValid;

                exps.Add(roomTypeId, exp);
            }

            return exps;
        }

        private IDictionary<string, Expression<Func<RoomOccupy, bool>>> BuildDayOccupyCountExp(DateTime bizDay, IList<RoomType> roomTypes)
        {
            IDictionary<string, Expression<Func<RoomOccupy, bool>>> exps = new Dictionary<string, Expression<Func<RoomOccupy, bool>>>();

            DateTime bizDayMax = bizDay.AddDays(1).AddSeconds(-1);

            foreach (var type in roomTypes)
            {
                string roomTypeId = type.RoomTypeID;

                string hotelId = type.HotelID;

                Expression<Func<RoomOccupy, bool>> exp = r => r.HotelID == hotelId
                                                              && r.RoomTypeID == roomTypeId
                                                              && r.StartDate <= bizDayMax
                                                              && r.IsValid;

                exps.Add(roomTypeId, exp);
            }

            return exps;
        }

        private IList<Order> BuildNoShowOrders(IList<Reservation> noShowResvation)
        {
            IEnumerable<Order> orders = new List<Order>();

            foreach (var resv in noShowResvation)
            {
                var noshow = resv.Order.Where(o => o.Status == EnumHelper.GetCode(OrderStatus.R));

                if(noshow.Any())
                {
                    orders = orders.Union(noshow);
                }
            }

            return orders.ToList();
        }

        private IList<RoomOccupy> BuildNoShowOccupies(IList<Reservation> noShowResvation)
        {
            IEnumerable<RoomOccupy> occupies = new List<RoomOccupy>();

            foreach (var resv in noShowResvation)
            {
                var noshow = resv.RoomOccupy.Where(o => o.OrderType ==(int)OrderType.Reserve && o.IsValid);

                if (noshow.Any())
                {
                    occupies = occupies.Union(noshow);
                }
            }

            return occupies.ToList();
        }
        
        private int CountPassNight(IEnumerable<RoomOccupy> resvOccupies)
        {
            int count = 0;

            foreach (var occupy in resvOccupies)
            {
                count += (occupy.EndDate - occupy.StartDate).Days;
            }

            return count;
        }

        private IDictionary<string, decimal> BuildAverageRoomPrice(IDictionary<string, decimal> rtRoomFeeAccounts, IList<Account> notRollAccounts, IList<Order> rollAccountOrders, IList<RoomType> roomTypes, IList<RollAccountOrderCount> rollAccountOrderCounts, HotelBizDay bizDay)
        {
            var fp = FormulaProvider.Instance;

            var formula= fp.GetFormula(FormulaProvider.AvgRoomPrice);

            IDictionary<string, decimal> prices = new Dictionary<string, decimal>();

            foreach (var roomtype in roomTypes)
            {
                var count= rollAccountOrderCounts.FirstOrDefault(c => c.Name == roomtype.RoomTypeID);
                
                decimal rtFeeSum = rtRoomFeeAccounts[roomtype.RoomTypeID];

                decimal price = 0;

                if (count != null)
                {
                    decimal rtCount = (decimal) (count.CheckInType_NS/formula.CheckInType_NS
                                                 + count.CheckInType_DR/formula.CheckInType_DR
                                                 + count.CheckInType_HR/formula.CheckInType_HR
                                                 + count.CheckInType_FR/formula.CheckInType_FR
                                                 + count.CheckInType_SU/formula.CheckInType_SU
                                                 + count.ExtraHalfDay/formula.Ledger_ExtraHalfDay
                                                 + count.ExtraFullDay/formula.Ledger_ExtraFullDay);

                    if(rtCount>0)
                    {
                        price = Math.Round(rtFeeSum / rtCount, 2);
                    }
                }
                prices.Add(roomtype.RoomTypeID, price);
            }

            return prices;
        }

        private IList<RollAccountOrderCount> CountRollAccountOrder(IList<Account> notRollAccounts, IList<Order> rollAccountOrders, IList<RoomType> roomTypes)
        {
            IList<RollAccountOrderCount> racs = new List<RollAccountOrderCount>();

            foreach (var roomtype in roomTypes)
            {
                RollAccountOrderCount rac = new RollAccountOrderCount();

                rac.Name = roomtype.RoomTypeID;

                rac.CheckInType_NS = rollAccountOrders.Count(o => o.CheckInType == EnumHelper.GetCode(CheckInType.NS) && o.RoomTypeID == roomtype.RoomTypeID);

                rac.CheckInType_DR = rollAccountOrders.Count(o => o.CheckInType == EnumHelper.GetCode(CheckInType.DR) && o.RoomTypeID == roomtype.RoomTypeID);

                rac.CheckInType_HR = rollAccountOrders.Count(o => o.CheckInType == EnumHelper.GetCode(CheckInType.HR) && o.RoomTypeID == roomtype.RoomTypeID);

                rac.CheckInType_FR = rollAccountOrders.Count(o => o.CheckInType == EnumHelper.GetCode(CheckInType.FR) && o.RoomTypeID == roomtype.RoomTypeID);

                rac.CheckInType_SU = rollAccountOrders.Count(o => o.CheckInType == EnumHelper.GetCode(CheckInType.SU) && o.RoomTypeID == roomtype.RoomTypeID);

                var rtOderIds = rollAccountOrders.Where(o => o.RoomTypeID == roomtype.RoomTypeID).Select(o => o.OrderID);

                rac.ExtraHalfDay = notRollAccounts.Count(a => rtOderIds.Contains(a.AccountRootID) && a.LedgerCode == LedgerCode.半日租);

                rac.ExtraFullDay = notRollAccounts.Count(a => rtOderIds.Contains(a.AccountRootID) && a.LedgerCode == LedgerCode.全日租);

                racs.Add(rac);
            }

            return racs;
        }

        /// <summary>
        /// 旧方法[作废]
        /// </summary>
        /// <param name="accountRoots"></param>
        /// <param name="roomTypes"></param>
        /// <param name="bizDay"></param>
        /// <returns></returns>
        private IDictionary<string, decimal> BuildAverageRoomPrice(IEnumerable<AccountRoot> accountRoots, IList<RoomType> roomTypes,HotelBizDay bizDay)
        {
            IDictionary<string, Expression<Func<PriceDetail, bool>>> exps = new Dictionary<string, Expression<Func<PriceDetail, bool>>>();

            DateTime day = bizDay.BizDay;

            DateTime dayMax = day.AddDays(1).AddSeconds(-1);

            DateTime tomorrowExit = day.AddDays(1).AddHours(GlobalConst.DEFAULT_CHECK_OUT_HOUR);

            string hotelId = bizDay.HotelID;

            IList<string> normalPriceRoomTypes = new List<string>();

            foreach(var rt in roomTypes)
            {
                //IList<string> orderIds = accountRoots.Where(a =>
                //                 a.Order.RoomTypeID == rt.RoomTypeID
                //                 && a.Order.DepartureDate >= tomorrowExit
                //                 && a.Order.ArrivalDate <= dayMax 
                //                 && a.Order.CheckInType == EnumHelper.GetCode(CheckInType.NS))
                //                 .Select(a => a.AccountRootID).ToList();

                //修改为只计算当前在住的房间
                IList<string> orderIds = accountRoots
                    .Where(a =>
                           a.Order.RoomTypeID == rt.RoomTypeID
                           && a.Status == EnumHelper.GetCode(AccountStatus.I)
                           && a.Order.CheckInType == EnumHelper.GetCode(CheckInType.NS))
                    .Select(a => a.AccountRootID).ToList();

                if (!orderIds.Any())
                {
                    normalPriceRoomTypes.Add(rt.RoomTypeID);
                    continue;
                }

                Expression<Func<PriceDetail, bool>> exp = o => o.BizDay == day && o.HotelID == hotelId && orderIds.Contains(o.OrderID);

                exps.Add(rt.RoomTypeID, exp);
            }

            IPriceDetailRepository pdr = container.Resolve<IPriceDetailRepository>();

            var prices= pdr.GetAverageRoomPrice(hotelId, exps);

            foreach(var rtId in normalPriceRoomTypes)
            {
                var pc = PriceCache.GetInstance();

                decimal price= pc.GetRoomPrice(hotelId, rtId, day);

                prices.Add(rtId,price);
            }

            return prices;
        }

        private IEnumerable<Order> UpdateStatusAndGetOrders(IList<Reservation> noShowResvation, IList<RoomOccupy> noShowOccupies, HotelBizDay bizDay)
        {
            IEnumerable<Order> noshowOrders = BuildNoShowOrders(noShowResvation);

            foreach(var order in noshowOrders)
            {
                order.Status = EnumHelper.GetCode(OrderStatus.X);
                order.ModifiedTime = DateTime.Now;
                order.EmployeeID = GlobalConst.NIGHT_AUDIT_SYSTEM;
            }

            foreach(var occupy in noShowOccupies)
            {
                occupy.IsNoShow = true;
                occupy.IsValid = false;
                occupy.CancelBizDay = bizDay.BizDay;
                occupy.ModifiedTime = DateTime.Now;
                occupy.EmployeeID = GlobalConst.NIGHT_AUDIT_SYSTEM;
            }

            foreach(var resv in noShowResvation)
            {
                resv.Status = EnumHelper.GetCode(ReserveStatus.N);
            }
            return noshowOrders;
        }
        
    }

    public class RollAccountOrderCount
    {
        public string Name { get;set;}

        public int CheckInType_NS { get; set; }

        public int CheckInType_DR { get; set; }

        public int CheckInType_HR { get; set; }

        public int CheckInType_FR { get; set; }

        public int CheckInType_SU { get; set; }

        public int ExtraHalfDay { get; set; }

        public int ExtraFullDay { get; set; }

    }

}
